package com.sneakxy.mybatis.commons.repository.support;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.binding.MapperMethod.ParamMap;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.mapping.SqlSource;
import org.apache.ibatis.scripting.xmltags.DynamicSqlSource;
import org.apache.ibatis.scripting.xmltags.TextSqlNode;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;

import com.sneakxy.mybatis.commons.annotations.Total;
import com.sneakxy.mybatis.commons.orm.MybatisEntityManager;
import com.sneakxy.mybatis.commons.repository.query.MybatisQueryMethod;
import com.sneakxy.mybatis.commons.utils.MybatisUtils;

/**
 * @author 潜行的青衣
 */
public class MybatisMapperRepositoryQuery extends AbstractMybatisRepositoryQuery {

	private Object mapper;
	
	private String pageCountQueryId;
	
	public MybatisMapperRepositoryQuery(
			MybatisEntityManager entityManager,
			MybatisQueryMethod queryMethod,
			Object mapper) {
		super(entityManager, queryMethod);
		this.mapper = mapper;
	}
	
	/* (non-Javadoc)
	 * @see org.springframework.data.repository.query.RepositoryQuery#execute(java.lang.Object[])
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
	@Override
	public Object execute(Object[] parameters) {
		Method method = this.getMethod();
		Object result = null;
		try {
			result = method.invoke(mapper, parameters);
			//添加分页总记录数的查询方式
			if(this.getQueryMethod().isPageQuery() || this.getQueryMethod().isSliceQuery()) {
				if(this.getPageCountQueryId() != null) {
					List content = ((Page) result).getContent();
					Long total = this.getEntityManager()
							.getSqlSessionTemplate()
							.selectOne(this.getPageCountQueryId(), createParameterObject(parameters, true));
					return new PageImpl(content, MybatisUtils.getPageable(parameters) ,total);
				}
			}
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		}
		return result;
	}

	protected String createMapperPageCountId() {
		StringBuilder idBuilder = new StringBuilder()
				.append("count_").append(this.getQueryMethod().getName()).append("-");
		String resultId = this.createStatementId(idBuilder.toString());
		this.pageCountQueryId = this.getEntityManager().getNamespacedId(resultId);
		return resultId;
	}
	
	public Object getMapper() {
		return mapper;
	}

	public String getPageCountQueryId() {
		return pageCountQueryId;
	}

	/* (non-Javadoc)
	 * @see com.sneakxy.mybatis.commons.repository.support.AbstractMybatisRepositoryQuery#doCreatePageCountQuery()
	 */
	@Override
	protected void doCreatePageCountQuery() {
		Total total = this.getMethod().getAnnotation(Total.class);
		StringBuilder countSql = new StringBuilder();
		if(total != null) {
			for(String value: total.value()) {
				countSql.append(value);
				countSql.append(" ");
			}
		} else {
			Select select = this.getMethod().getAnnotation(Select.class);
			if(select != null) {
				StringBuilder sql = new StringBuilder();
				for(String value: select.value()) {
					sql.append(value);
				    sql.append(" ");
				}
				String lowerSql = sql.toString().toLowerCase();
				Pattern pattern = Pattern.compile("(select.*from)");
				Matcher matcher = pattern.matcher(lowerSql);
				if(matcher.find()) {
					countSql.append(sql.substring(0, matcher.start()));
					countSql.append("SELECT COUNT(*) FROM");
					countSql.append(sql.substring(matcher.end()));
				}
			}
		}
		if(countSql.length() > 0) {
			SqlSource sqlSource = new DynamicSqlSource(getEntityManager().getConfiguration(), new TextSqlNode(countSql.toString()));
//			SqlSource sqlSource = builder.parse(countSql.toString(), ParamMap.class, new HashMap<String, Object>());
			String id = this.createMapperPageCountId();
			SqlCommandType sqlCommandType  = SqlCommandType.SELECT;
			this.getEntityManager().addMappedStatement(
					id, sqlSource, sqlCommandType, 
					ParamMap.class, Long.class);
		}
	}

}
